#include <qxmmp/lib/src/QXmppLogger.h>
#include <qxmmp/lib/src/QXmppMessage.h>
#include <qxmmp/lib/src/QXmppRosterManager.h>
#include <qxmmp/lib/src/QXmppVCardManager.h>

#include "contactinfo.h"
#include "contactsmodel.h"
#include "xmppprofile.h"

IXmppProfile::IXmppProfile(QObject * parent) :
        IProfile(parent)
{
#ifndef NODEBUG
    QXmppLogger::getLogger()->setLoggingType(QXmppLogger::StdoutLogging);
#endif
}

QString IXmppProfile::networkId() const
{
    return "xmpp";
}

CProfile * IXmppProfile::createInstance(const CAuthInfo &authInfo)
{
    return new XmppProfile(authInfo);
}

QString IVkontakteProfile::networkId() const
{
    return "vkontakte";
}

bool IVkontakteProfile::match(const CAuthInfo &authInfo)
{
    return (authInfo.host() == "vk.com"
            || authInfo.host() == "vkmessenger.com");
}

bool IVkontakteProfile::fillAuthInfo(CAuthInfo *authInfo)
{
    authInfo->setNetworkId(networkId());
    authInfo->setHost("vkmessenger.com");
    authInfo->setDomain("vk.com");
    authInfo->setPort(5222);

    return true;
}

//////////////// XmmpProfile ///////////////

XmppProfile::XmppProfile(const CAuthInfo &authInfo, IProfile *parent) :
    CProfile(authInfo, parent),
    m_client(new QXmppClient(this))
{
    connect(m_client, SIGNAL(connected()), this, SLOT(clientConnected()));
    connect(m_client, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
    connect(m_client, SIGNAL(error(QXmppClient::Error)), this, SLOT(clientError(QXmppClient::Error)));
    connect(&m_client->rosterManager(), SIGNAL(rosterReceived()), this, SLOT(rosterReceived()));
    connect(&m_client->rosterManager(), SIGNAL(presenceChanged(QString,QString)), this, SLOT(presenceChanged(QString,QString)));
    connect(&m_client->vCardManager(), SIGNAL(clientVCardReceived()), this, SLOT(clientVCardReceived()));

    connect(m_client, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(clientMessageReceived(QXmppMessage)));
}

ContactInfo XmppProfile::resolveUid(const QString &uid) const
{
    ContactInfo * ci = m_contactsModel->getContact(uid, networkId());
    ContactInfo cinfo;

    if (ci)
        return *ci;
    else {
        // FIX ME: contact not in roster
    }
    return cinfo;
}

void XmppProfile::connectToNetwork()
{
//    QXmppConfiguration conf;

//    conf.setHost(m_authInfo.host());
//    conf.setDomain(m_authInfo.domain());
//    //conf.setResource(m_authInfo.resource());
//    conf.setPort(authInfo().port());
//    conf.setUser(m_authInfo.user());
//    conf.setPasswd(m_authInfo.password());

    setState(Connecting);
    m_client->connectToServer(m_authInfo.host(), m_authInfo.user(),
                              m_authInfo.password(), m_authInfo.domain(), m_authInfo.port());
}

void XmppProfile::disconnectFromNetwork()
{
    m_client->disconnectFromServer();
}

void XmppProfile::sendMessage(const QString &message, const ContactInfo &to)
{
    m_client->sendMessage(to.id(), message);
}

void XmppProfile::clientConnected()
{
    setState(LoadingContacts);
    m_client->vCardManager().requestClientVCard();
}

void XmppProfile::clientDisconnected()
{
    setState(NotConnected);
}

void XmppProfile::clientError(QXmppClient::Error error)
{
    if (error == QXmppClient::SocketError)
        setError(SocketError);
    else if (error == QXmppClient::KeepAliveError)
        setError(ConnectionError);
    else if (error == QXmppClient::XmppStreamError) {
        if (m_client->xmppStreamError() == QXmppStanza::Error::NotAuthorized)
            setError(AuthorizationError);
        else
            setError(OtherError);
    }
}

void XmppProfile::clientVCardReceived()
{
    ContactInfo profileInfo(m_client->configuration().jidBare(), networkId());
    QXmppVCard clientCard = m_client->vCardManager().clientVCard();

    profileInfo.setName(clientCard.fullName());
}

void XmppProfile::rosterReceived()
{
    QStringList uids = m_client->rosterManager().getRosterBareJids();

    foreach (const QString &uid, uids) {
        rosterChanged(uid);
    }

    setState(ContactsLoaded);
}

void XmppProfile::setPresence(ContactInfo * contact, const QString &uid)
{
    QMap<QString, QXmppPresence> xmppPresences = m_client->rosterManager().getAllPresencesForBareJid(uid);
    ContactInfo::Presence presence = ContactInfo::Offline;

    QMap<QString, QXmppPresence>::Iterator it = xmppPresences.begin();
    while (it != xmppPresences.end()) {
        QXmppPresence::Status status = it.value().status();
        switch (status.type()) {
        case QXmppPresence::Status::Offline:
            break;
        case QXmppPresence::Status::Online:
            presence = ContactInfo::Online;
            break;
        default:
            presence = ContactInfo::Away;
        }
        break;
    }
    contact->setPresence(presence);
}

void XmppProfile::rosterChanged(const QString &bareJid)
{
    ContactInfo * contact = m_contactsModel->getContact(bareJid, networkId());
    if (!contact)
        contact = m_contactsModel->addContact(bareJid, networkId());
    QXmppRosterIq::Item rosterItem = m_client->rosterManager().getRosterEntry(bareJid);

    setPresence(contact, bareJid);
    contact->setName(rosterItem.name());
}

void XmppProfile::presenceChanged(const QString &bareJid, const QString &resource)
{
    ContactInfo * contact = m_contactsModel->getContact(bareJid, networkId());
    if (contact) {
        setPresence(contact, bareJid);
    }
}

void XmppProfile::clientMessageReceived(const QXmppMessage &message)
{
    CMessage msg(networkId());

    msg.setFrom(message.from());
    msg.setTo(message.to());
    msg.setTime(message.stamp());
    msg.setText(message.body());

    emit messageReceived(msg);
}
